package it.esselunga.iphone.client;


import com.google.code.gwt.database.client.*;
import com.google.code.gwt.storage.client.Storage;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.query.client.Function;
import com.google.gwt.query.client.GQuery;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;

import java.util.Date;
import java.util.logging.Logger;

import static com.google.gwt.query.client.GQuery.$;

public class Application implements EntryPoint {

    private final static Logger LOG = Logger.getLogger(Application.class.getName());

    private final static Storage localStorage = Storage.getLocalStorage();
    private final static Storage sessionStorage = Storage.getSessionStorage();
    private static Database db;
    private long DAY_IN_MILLIS = 86400000;
    private JQTouch jQT = JQTouch.instance();
    
    public void onModuleLoad() {
        if (!Storage.isSupported() || !Database.isSupported()) {
            Window.alert("HTML5 non supportato correttamente !!!!");
            return;
        }

        resizeScreen();

        //init();
        initializeDB();

        bindEvents();

    }

    private void resizeScreen() {
        Window.scrollTo(0, 0);
        //resizeTo(320,480); // iPhone
        Window.resizeTo(320, 960); // iPhone x 2

    }

    public static native void jQTgoBack() /*-{
        $wnd.jQT.goBack();
    }-*/;

    public static native void jQTslideUp(Element el) /*-{
        el.slideUp();
    }-*/;

    private void bindEvents() {
        // Salva su Settings
        $("#settings a.button").click(new Function() {
            @Override
            public boolean f(Event e) {
                saveSettings();

                return false;
            }
        });

        // Bottone Settings
        $("#home a.button").click(new Function() {

            @Override
            public boolean f(Event e) {
                onShowSettings();

                return true;
            }
        });

        // Click su una data
        $("#dates li a").click(new Function(){
            @Override
            public boolean f(Event e) {

                LOG.fine("cliccata data");
                onDateSelected(e);

                return true;
            }
        });

        // Salva su Create Entry
        $("#createEntry input.submit").click(new Function(){
            @Override
            public boolean f(Event e) {
                onCreateEntry();

                return false;
            }
        });
    }

    private void initializeDB() {
        String shortName = "Kilo";
        String version = "1.0";
        String displayName = "Kilo";
        int maxSize = 65536;

        db = Database.openDatabase(shortName, version, displayName, maxSize);

        db.transaction(new TransactionCallback() {
            public void onTransactionStart(SQLTransaction tx) {
                tx.executeSql("CREATE TABLE IF NOT EXISTS entries " +
                              "  (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, " +
                              "   date DATE NOT NULL, food TEXT NOT NULL, " +
                              "   calories INTEGER NOT NULL)" , null);
            }
            public void onTransactionFailure(SQLError error) {
                errorHandler("initializeDB", error);
            }
            public void onTransactionSuccess() {
                // Proceed when successfully committed...
            }
        });
    }

    private void onCreateEntry() {
        final String date = sessionStorage.getItem("currentDate");
        final String calories = $("#calories").val();
        final String food = $("#food").val();

        db.transaction(new TransactionCallback() {
            public void onTransactionStart(SQLTransaction tx) {
                tx.executeSql("INSERT INTO entries " +
                        "(date, calories, food) " +
                        "VALUES (?, ?, ?);" , new Object[]{date, calories, food});
            }
            public void onTransactionFailure(SQLError error) {
                errorHandler("createEntry", error);
            }
            public void onTransactionSuccess() {
                clearCreateEntry();
                refreshEntries();

                jQT.goBack();
            }
        });
    }

    private void clearCreateEntry() {
        $("#food").val("");
        $("#calories").val("");
    }

    private void errorHandler(String method, SQLError error) {
        String errorMessage = "Method: "+method+"Oops. Error was "+error.getMessage()+" (Code "+error.getCode()+")";
        LOG.fine(errorMessage);
        Window.alert(errorMessage);
    }

    private void saveSettings() {
        localStorage.setItem("age", $("#age").val());
        localStorage.setItem("budget", $("#budget").val());
        localStorage.setItem("weight", $("#weight").val());

        jQT.goBack();
    }

    private void onShowSettings() {
        $("#age").val(localStorage.getItem("age"));
        $("#budget").val(localStorage.getItem("budget"));
        $("#weight").val(localStorage.getItem("weight"));
    }

    private void onDateSelected(Event e) {
        long id = Long.parseLong(getElement(e).getId());
        LOG.fine("Selected Date: "+id);
        
        long now = System.currentTimeMillis();
        long calculated = now - DAY_IN_MILLIS*id;

        DateTimeFormat formatter = DateTimeFormat.getFormat(DateTimeFormat.PredefinedFormat.DATE_SHORT);

        final Date date = new Date(calculated);
        LOG.fine("Date: "+date);

        sessionStorage.setItem("currentDate", formatter.format(date));

        refreshEntries();
    }

    private void refreshEntries() {
        final String currentDate = sessionStorage.getItem("currentDate");

        $("#date h1").text(currentDate);
        $("#date ul li").remove();

        db.transaction(
                new TransactionCallback() {
            public void onTransactionStart(SQLTransaction tx) {
                tx.executeSql("SELECT * FROM entries " +
                              "WHERE date = ? ORDER BY food;" ,
                        new Object[]{currentDate},
                    new StatementCallback<Entry>(){

                      @Override
                      public void onSuccess(SQLTransaction sqlTransaction, SQLResultSet<Entry> entrySQLResultSet) {
                            for (int i=0; i < entrySQLResultSet.getRows().getLength(); i++) {
                                Entry row = entrySQLResultSet.getRows().getItem(i);
                                final GQuery newEntryRow = $("#entryTemplate").clone();
                                final String attrId = "entryTemplate" + i;
                                newEntryRow.attr("id", attrId);
                                newEntryRow.removeAttr("id");
                                newEntryRow.removeAttr("style");
                                final int id = row.getId();
                                newEntryRow.data("attrId", id);
                                newEntryRow.appendTo("#date ul");
                                newEntryRow.find(".label").text(row.getFood());
                                newEntryRow.find(".calories").text(Integer.toString(row.getCalories()));

                                newEntryRow.find(".delete").click(new Function(){
                                    @Override
                                    public boolean f(Event e) {
                                        deleteEntryById(id);

                                        jQT.slideUp(attrId);//jQTslideUp(clickedEntry);

                                        return true;
                                    }

                                });
                            }
                      }

                      @Override
                      public boolean onFailure(SQLTransaction sqlTransaction, SQLError sqlError) {
                          return true;
                      }
                    });
            }
            public void onTransactionFailure(SQLError error) {
                errorHandler("refreshEntries", error);
            }
            public void onTransactionSuccess() {
            }
        });
    }

    private void deleteEntryById(final int id) {

        db.transaction(new TransactionCallback() {
            public void onTransactionStart(SQLTransaction tx) {
                LOG.fine("Sto cancellando la Entry con id: "+id);
                tx.executeSql("DELETE FROM entries WHERE id=?;" , new Object[]{id});
            }
            public void onTransactionFailure(SQLError error) {
                errorHandler("createEntry", error);
            }
            public void onTransactionSuccess() {
                refreshEntries();
            }
        });
    }

    private Element getElement(Event e) {
        NativeEvent ne = e.cast();

        return Element.as(ne.getCurrentEventTarget());
    }
}
